#include <AST/scope/scope.hpp>
#include <iostream>
#include <llvm/IR/DerivedTypes.h>

using namespace std;

ScopeGuard::ScopeGuard(Scope &scope) : scope(scope) {
    this->scope.push();
}

ScopeGuard::~ScopeGuard() {
    scope.pop();
}

void Scope::push() {
    BreakPoint bp = scopeStack.empty() ? nullptr : scopeStack.top().breakPoint;
    ContinuePoint cp =
        scopeStack.empty() ? nullptr : scopeStack.top().continuePoint;
    scopeStack.emplace(
        ScopeInfo{NameVector(), DeletedValue(), NameSet(), bp, cp});
}

void Scope::pop() {
    {
        const auto &all = scopeStack.top();
        const auto &addNames = all.nameVector;
        const auto &delNames = all.deletedValues;
        // 删除当前作用域
        for (const string &name : addNames) { namedValuePtr.erase(name); }
        // 还原被删除的值
        for (const auto &[name, value] : delNames) {
            namedValuePtr[name] = value;
        }
    }
    scopeStack.pop();
}

bool Scope::insertValue(const std::string &name, llvm::Value *value) {
    auto &all = scopeStack.top();
    auto &addNames = all.nameVector;
    auto &delNames = all.deletedValues;
    auto &existNames = all.nameSet;
    // 检查当前作用域无重复
    if (existNames.contains(name)) return false;
    // 留下增加记录
    addNames.push_back(name);
    // 留下已有名字记录
    existNames.insert(name);
    // 检查外部作用域重复命名，替换之
    if (auto it = namedValuePtr.find(name); it != namedValuePtr.end()) {
        // 如果已有值，则替换之，并留下记录
        delNames.push_back(make_pair(name, it->second));
        it->setValue(value);
    } else {
        // 否则直接插入
        namedValuePtr[name] = value;
    }
    return true;
}

llvm::Value *Scope::findValue(const std::string &name) const {
    auto it = namedValuePtr.find(name);
    if (it == namedValuePtr.end()) return nullptr;
    return it->second;
}

ScopeGuard Scope::guard() {
    return ScopeGuard(*this);
}

bool Scope::isClear() const {
    return scopeStack.empty() && namedValuePtr.empty();
}

bool Scope::insertProto(const std::string &name, PrototypeAST *proto) {
    auto it = protos.find(name);
    if (it != protos.end()) return it->second == proto;
    protos[name] = proto;
    return true;
}

const PrototypeAST *Scope::findProto(const std::string &name) const {
    auto it = protos.find(name);
    return it != protos.end() ? it->second : nullptr;
}

bool Scope::insertStruct(const std::string &name, StructDef &&structDef) {
    auto it = structs.find(name);
    if (it != structs.end()) return it->second == structDef;
    structs.insert(make_pair(name, move(structDef)));
    return true;
}

bool Scope::hasStruct(const std::string& name) {
    return structs.find(name) != structs.end();
}

const StructDef &Scope::findStruct(const std::string &name) const {
    auto it = structs.find(name);
    return it->second;
}

llvm::Type* Scope::findType(const std::string& typeName) const {
    return findStruct(typeName).type;
}

void Scope::setBreakPoint(const BreakPoint block) {
    BreakPoint &bp = scopeStack.top().breakPoint;
    bp = block;
}

Scope::BreakPoint Scope::getBreakPoint() const {
    return scopeStack.top().breakPoint;
}

void Scope::setContinuePoint(const ContinuePoint block) {
    ContinuePoint &cp = scopeStack.top().continuePoint;
    cp = block;
}

Scope::ContinuePoint Scope::getContinuePoint() const {
    return scopeStack.top().continuePoint;
}

std::optional<unsigned int>
StructDef::findIndex(const std::string &field) const {
    const auto it = fieldIndex.find(field);
    if (it == fieldIndex.end()) return std::nullopt;
    // XXX: 没有判空
    return it->second;
}

StructDef::StructDef(std::string name, llvm::StructType *type)
    : name(std::move(name)), type(type) {
    fieldIndex.clear();
}

void StructDef::addField(const std::string &field) {
    fieldIndex[field] = indexCount++;
}
